﻿using System;
using System.Collections.Specialized;
using CodePlex.SharePointInstaller.Configuration;

namespace CodePlex.SharePointInstaller.Tests
{
    #region MSTest/NUnit Compatability

    // Define MSTEST as a conditional compilation symbol in your DEBUG build then you can do testing with your DEBUG test builds
    // directly in Visual Studio. This also means you can debug your tests.
    // Note, if you use any MSTest specific functionality that is NOT available in NUnit, it WILL NOT WORK in NUnit!
    // Such functionality includes any direct use of TestContext, any web service related attributes and other stuff I haven’t yet dug up but know exists!
    // Any NUnit functionality WILL work in MSTest since the NUnit Assert is THE Assert class and the NUnit.Framework assembly
    // is referenced and used. Any test assemblies that have been built with MSTEST specified will NOT work in NUnit, therefore
    // it is suggested that from time to time you create a Release build of your tests. If you have used any MSTest only functionality
    // then your build (and consequently the build of your test on the build server) will FAIL. Check it!
    // Note, ensure your test is in DEBUG build before using VS to create unit tests or it will add MSTest usings into your Release build!

#if !MSTEST
#if DEBUG
#error ******* DEBUG builds should define MSTEST as a Conditional Compilation Symbol in Project Properties->Build Tab ************
#else
#warning ******* Test is compiled for NUnit ************
#endif
    using NUnit.Framework;
    using NUnit.Framework.Constraints;

    // The following using declarations allow the Visual Studio autogenerated framework to actually use NUnit attributes.
    // This means that where in the test code below we see the VS generated [TestClass()] attribute, it is “really” [TestFixture],
    // i.e. NUnit style with MSTest syntax – which gets autogenerated and is thus easier to “write” as VS does it for you.  

    using TestClass = NUnit.Framework.TestFixtureAttribute;
    using TestMethod = NUnit.Framework.TestAttribute;
    using TestCleanup = NUnit.Framework.TearDownAttribute;
    using TestInitialize = NUnit.Framework.SetUpAttribute;
    using ClassCleanup = NUnit.Framework.TestFixtureTearDownAttribute;
    using ClassInitialize = NUnit.Framework.TestFixtureSetUpAttribute;
    using TestContext = System.Object;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.Web;
using Category = Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute;
#endif

    // Since NUnit’s Assert is a superset of MSTest’s Assert, we’ll treat Assert as the NUnit version and get the extra goodies.
    using Assert = NUnit.Framework.Assert;

    #endregion

    /// <summary>
    /// Unit test for the commands parser
    /// </summary>
    [TestClass]
    public class CommandToRunTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        private void CheckNameParameter(string name, StringDictionary result)
        {
            CheckParametersPresent("o", name, result);
        }

        private void CheckNumberOfParameters(int number, StringDictionary result)
        {
            Assert.IsTrue(result.Count == number);
        }

        private void CheckParametersPresent(string key, string value, StringDictionary result)
        {
            Assert.IsTrue(result.ContainsKey(key) && result[key] == value);
        }

        [TestMethod]
        public void TestSimpleParameter()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "commandName",
                Parameters = "-key1 val1"
            };
            var result = cmd.ParseCommandLine();
            CheckNameParameter("commandName", result);
            CheckNumberOfParameters(2, result);
            CheckParametersPresent("key1", "val1", result);
        }

        [TestMethod]
        public void TestMultipleKeys()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "anotherName",
                Parameters = "-key1 -key2 -key3"
            };
            var result = cmd.ParseCommandLine();
            CheckNameParameter("anotherName", result);
            CheckNumberOfParameters(4, result);
            CheckParametersPresent("key1", String.Empty, result);
            CheckParametersPresent("key2", String.Empty, result);
            CheckParametersPresent("key3", String.Empty, result);
        }

        [TestMethod]
        public void TestMultipleKeyValuesPairs()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "name1",
                Parameters = "-key1 val1 -key2 val2 -key3 val3"
            };
            var result = cmd.ParseCommandLine();
            CheckNameParameter("name1", result);
            CheckNumberOfParameters(4, result);
            CheckParametersPresent("key1", "val1", result);
            CheckParametersPresent("key2", "val2", result);
            CheckParametersPresent("key3", "val3", result);
        }

        [TestMethod]
        public void TestMixedParameters()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "nmn",
                Parameters = "-key1 val1 -keyBool -keyBool2 -key2-new val2 -key3"
            };
            var result = cmd.ParseCommandLine();
            CheckNameParameter("nmn", result);
            CheckNumberOfParameters(6, result);
            CheckParametersPresent("key1", "val1", result);
            CheckParametersPresent("key2-new", "val2", result);
            CheckParametersPresent("keyBool", "", result);
            CheckParametersPresent("keyBool2", "", result);
            CheckParametersPresent("key3", "", result);
        }

        [TestMethod]
        public void TestEscapedParameters()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "complexCommand",
                Parameters = "-key1 \"two words\" -key2 \"-key3 val3\" -keyLast"
            };
            var result = cmd.ParseCommandLine();
            CheckNameParameter("complexCommand", result);
            CheckNumberOfParameters(4, result);
            CheckParametersPresent("key1", "two words", result);
            CheckParametersPresent("key2", "-key3 val3", result);
            CheckParametersPresent("keyLast", "", result);
        }

        [TestMethod]
        public void TestExtraWhitespace()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "spacyCommand",
                Parameters = "   -key1   \"more and more\"    -key2   \"-key3 val3\" -keyLast   "
            };
            var result = cmd.ParseCommandLine();
            CheckNameParameter("spacyCommand", result);
            CheckNumberOfParameters(4, result);
            CheckParametersPresent("key1", "more and more", result);
            CheckParametersPresent("key2", "-key3 val3", result);
            CheckParametersPresent("keyLast", "", result);
        }

        [TestMethod]
        public void TestIncorrectSyntaxMultipleValuesUnquoted()
        {
            CommandToRun cmd = new CommandToRun
            {
                Name = "wrongCommand",
                Parameters = "-key1 val1 val2 val3"
            };
            var result = cmd.ParseCommandLine();
            Assert.IsNull(result);
        }
    }
}
